package com.ccys.db.interceptor;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.ccys.common.annotations.LogAnalysisAnnexTag;
import com.ccys.common.util.Kit;
import com.ccys.db.cache.UserUtil;
import com.ccys.db.config.SpringContextUtil;
import com.ccys.db.service.BaseTblAuditTrailWebService;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import net.sf.json.JSONObject;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.stream.Collectors;

/**
 * 拦截器
 */
@Slf4j
@EqualsAndHashCode
@Data
@Accessors(chain = true)
@Intercepts({@Signature(
        type = Executor.class,
        method = "update",
        args = {MappedStatement.class, Object.class})})
@Component
public class CustomInterceptor implements Interceptor {
	@Autowired
	private BaseTblAuditTrailWebService baseTblAuditTrailWebService;


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 获取sql命令
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();

        // 获取参数
        Object parameter = invocation.getArgs()[1];
        if(parameter==null){
            return invocation.proceed();
        }
        List<Field> allField = getAllField(parameter.getClass());
        String objId = "";
        String objSubId = "";
        Integer action = 0;
        Integer realAction = null;
        boolean flag = false;
        for (Field field : allField) {
            try {
                //log.info("field:{}",field.getName());
                field.setAccessible(true);
                LogAnalysisAnnexTag logAnalysisAnnexTag = field.getAnnotation(LogAnalysisAnnexTag.class);
                if(logAnalysisAnnexTag != null && logAnalysisAnnexTag.analysis()) {
                    Object value = field.get(parameter);
                    if(value != null) {
                        objSubId = value.toString();
                    }

                }
                if(field.get(parameter)==null){
                    if(SqlCommandType.INSERT.equals(sqlCommandType)){
                        switch (field.getName()){
                            case "createdOn":
                                //todo 待确定是否根据当前用户的时区来
                                field.set(parameter,new Date());
                                break;
                            case "createdOnUtc":
                                field.set(parameter, System.currentTimeMillis());
                                break;
                            case "createdBy":
                                field.set(parameter, UserUtil.getCurLoginUserId());
                                break;
                            case "id":
                                objId = IdUtil.randomUUID();
                                field.set(parameter, objId);
                                break;
                            case "delFlag":
                                field.set(parameter, 0);
                                break;
                            default:
                                break;
                        }
                    }
                    if(SqlCommandType.UPDATE.equals(sqlCommandType)){
                        switch (field.getName()){
                            case "updatedOn":
                                //todo 待确定是否根据当前用户的时区来
                                field.set(parameter,new Date());
                                break;
                            case "updatedOnUtc":
                                field.set(parameter, System.currentTimeMillis());
                                break;
                            case "updatedBy":
                                field.set(parameter, UserUtil.getCurLoginUserId());
                                break;
                            default:
                                break;
                        }
                    }
                }else{
                    if("id".equals(field.getName())) {
                        objId = field.get(parameter).toString();
                    }
                    if("delFlag".equals(field.getName())) {
                        if("1".equals(field.get(parameter).toString())) {
                            realAction = 2;
                        }
                    }

                }
            }catch (Exception e){
                log.error("赋值异常:",e);
            }finally {
                field.setAccessible(false);
            }
        }
        if(SqlCommandType.INSERT.equals(sqlCommandType)){
            flag = true;
            action = 0;
        }else if(SqlCommandType.UPDATE.equals(sqlCommandType)){
            action = 1;
            flag = true;
        } else  if(SqlCommandType.DELETE.equals(sqlCommandType)){
            action = 2;
            flag = true;
        }
        if(realAction != null) {
            action = realAction;
        }
        if(parameter instanceof String) {
            objId = parameter.toString();

        }
        if(flag) {
            handleLog(invocation,objId,objSubId,action);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }

    public static List<Field> getAllField(Class<?> classes) {
        List<Field> fieldList = new ArrayList<>();
        while (classes != Object.class) {
            fieldList.addAll(Arrays.stream(classes.getDeclaredFields()).collect(Collectors.toList()));
            //获取父类
            classes = classes.getSuperclass();
        }
        return fieldList;
    }

    public void handleLog(Invocation invocation,String objId,String objSubId,Integer action) {
        try {
            String jsonInfo = "";
            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            Object parameter = invocation.getArgs()[1];
            if(parameter != null){
                jsonInfo = JSON.toJSONString(parameter);
            }
            if(parameter instanceof String) {
                JSONObject  jsonObject = new JSONObject();
                jsonObject.put("id", parameter.toString());
                jsonInfo = jsonObject.toString();

            }
            String tableName = "";
            BoundSql boundSql = mappedStatement.getBoundSql(parameter);
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            String sql = boundSql.getSql();
            if(sql.contains("INSERT INTO tbl_audit_trail")) {
                //递归调用
                return;
            }
            Object[] parameterValues = new Object[parameterMappings.size()];
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                String property = parameterMapping.getProperty();
                Object value = null;
                if (boundSql.hasAdditionalParameter(property)) {
                    value = boundSql.getAdditionalParameter(property);
                } else if (parameter == null) {
                    value = null;
                } else if (mappedStatement.getConfiguration().getTypeHandlerRegistry().hasTypeHandler(parameter.getClass())) {
                    value = parameter;
                } else {
                    MetaObject metaObject = mappedStatement.getConfiguration().newMetaObject(parameter);
                    value = metaObject.getValue(property);
                }
                parameterValues[i] = value;
            }
            for (Object p : parameterValues) {
                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(String.valueOf(p)));
            }
            //log.info("实际执行的sql=" + sql);

            if (sql != null) {
                // 解析 SQL 语句中的表名
                tableName = Kit.parseTableName(sql);
            }
            if(this.baseTblAuditTrailWebService == null) {
                this.baseTblAuditTrailWebService = SpringContextUtil.getApplicationContext().getBean(BaseTblAuditTrailWebService.class);
            }
            this.baseTblAuditTrailWebService.saveTblAuditTrail(objId, objSubId, action, jsonInfo, tableName);
        }catch (Exception e) {
            log.error("写入操作日志失败");
        }
    }

}
